import React, {
  useState,
  useEffect,
  useRef,
  useImperativeHandle,
} from 'react'
import { info } from '@/library/tool-kits/log'
import { renderToBody } from './render-to-body'

type ImperativeProps = {
  visible?: boolean
  onClose?: () => void
  afterClose?: () => void
}

type TargetElement = React.ReactElement<ImperativeProps>

export type ImperativeHandler = {
  close: () => void
  replace: (element: TargetElement) => void
}

export const renderImperatively = (element: TargetElement): ImperativeHandler => {
  const Wrapper = React.forwardRef<ImperativeHandler>((_, ref) => {
    const [visible, setVisible] = useState(false)
    const closedRef = useRef(false)
    const [elementToRender, setElementToRender] = useState(element)
    const keyRef = useRef(0)
    const afterClose = () => {
      info('Imperatively', 'effect `afterClose`')
      // eslint-disable-next-line no-use-before-define
      unmount() // 必须销毁container元素
      elementToRender.props.afterClose?.()
    }
    const onClose = () => {
      info('Imperatively', 'effect `onClose`')
      closedRef.current = true
      setVisible(false)
      elementToRender.props.onClose?.()
    }
    useEffect(() => {
      if (!closedRef.current) {
        setVisible(true)
      } else {
        info('Imperatively', 'unknown function')
        afterClose()
      }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    useImperativeHandle(ref, () => ({
      close: onClose,
      replace: (ele) => {
        keyRef.current += 1
        elementToRender.props.afterClose?.()
        setElementToRender(ele)
      },
    }))
    return React.cloneElement(elementToRender, {
      ...elementToRender.props,
      key: keyRef.current,
      visible,
      onClose, // 供 Dialog 使用
      afterClose,
    })
  })
  const wrapperRef = React.createRef<ImperativeHandler>()
  const unmount = renderToBody(<Wrapper ref={wrapperRef} />)
  return {
    close: async () => {
      if (!wrapperRef.current) {
        info('Imperatively', 'wrapper is not mounted yet')
        unmount()
      } else {
        wrapperRef.current?.close()
      }
    },
    replace: (ele) => {
      wrapperRef.current?.replace(ele)
    },
  }
}
